//
// Created by 抑~风 on 2022/11/20.
//

#include"regexp.h"
#include<fstream>
#include<iostream>
#include<unordered_set>

using namespace std;
namespace CWJ {

    void show(RegexParser::ASTNodePtr root, string text) {
        if (root == nullptr) return;
        auto childs = root->getChilds();
        auto s = root->getContext();
        cout << text << (unsigned int) root->getType() << ":" << " from " << s.getFrom() << " to " << s.getTo()
             << " min: " << root->getMinTime() << " max: " << root->getMaxTime()<<" isOppo: "<<s.isOpposite1() << endl;
        for (auto &a : childs) {
            show(dynamic_pointer_cast<RegexParser::ASTNode>(a), text + "    ");
        }
    }

    void showNfaNode(NfaGraph::NfaNodePtr root) {
        string text = "    ";
        cout << root->getUid() << " " << root->isAc1() << endl;
        for (auto &o : root->getVers()) {
            cout << text << o.first->getUid() << endl;
        }
    }

    unordered_set<NfaGraph::NfaNodePtr> st;

    void showGraph(NfaGraph::NfaNodePtr root) {
        cout << "---" << endl;
        if (st.count(root)) return;
        st.insert(root);
        string text = "    ";
        showNfaNode(root);
        for (auto &o : root->getVers()) {
            showGraph(o.first);
        }
    }

    bool CWJLever::readRule(string path) {
        vector<string>rules;
        this->cUtil.readRule(path,rules);
        for(auto& str : rules){
            int ind = str.find(':');
            string name = str.substr(0,ind),rule = str.substr(ind+1);
//            cout<<name<<" "<<rule<<endl;
            this->ruleMap[name] = rule;
            this->ruleOrders.push_back(name);
        }

        this->updateNfaGraph();

        return true;

    }

    void CWJLever::updateNfaGraph() {
        NfaGraph::NfaNodePtr startState = nullptr, endState = nullptr;
        this->graph.setStartState(startState = make_shared<NfaNode>());
        this->graph.setEndState(endState = make_shared<NfaNode>(true));
        for (auto one: this->ruleOrders) {
            auto &a = this->ruleMap[one];
            RegexpLever lever{};
            RegexParser parser{};
            NfaGraph tmp{};
            auto tokens = lever.tokenize(a);
            auto tree = parser.parse(tokens);
//            show(tree,"    ");
            tmp.regexAST2nfaGraph(tree);
            startState->addVer(tmp.getStartState(), NfaEdge());
//            该语句加了就无法判断到达那个token了
//            tmp.getEndState()->addVer(tmp.getEndState(),NfaEdge());
            this->tokenMap[tmp.getEndState()] = one;
//            if (one == "minus") {
//                cout << one << endl;
//                st.clear();
//                showGraph(tmp.getStartState());
//            }
        }

//        showGraph(graph.getStartState());

    }

    CWJLever::TokenList CWJLever::tokenize(string script) {

        this->clear();

        int start = 0;
        unordered_set<NfaGraph::NfaNodePtr> st;
        while (start < script.size()) {
            if (script[start] == ' ' || script[start] == '\t' || script[start] == '\n') {
                start++;
                continue;
            }

            st.clear();
            if (!this->tokenize(script, this->graph.getStartState(), start, start, st)) {
                cout << start << " " << script[start] << " " << script.size() << endl;
                for(auto t : this->tokens){
                    cout<<t.getType()<<" "<<t.getConText()<<endl;
                }

                throw runtime_error("CWJLever::tokenize failed");
            }
//            cout<<this->tokens.back().getType()<<this->tokens.back().getConText()<<endl;

        }
        return this->tokens;
    }

    bool CWJLever::tokenize(string &script, NfaGraph::NfaNodePtr state, int ind, int &start,
                            unordered_set<NfaGraph::NfaNodePtr> &st) {
        auto ptr = state.get();
        if(!state->isGreedy1() && state->isAc1()){
            this->tokens.emplace_back(this->tokenMap[state], -1, script.substr(start, ind - start));
            start = ind;
            return true;
        }


        auto edgesOrder = state->getEdgesOrder();
        auto vers = state->getVers();
        char c = 0;
        for (const auto& v : edgesOrder) {
            auto &edges = vers[v];
            for(auto& e : edges) {
                if (e.isFilterNull()) {

                    if (tokenize(script, v, ind, start, st)) {
                        return true;
                    }
                } else if (ind >= script.size()) {
                    continue;
                } else if (c = script[ind], e.match(c)) {
                    if (tokenize(script, v, ind + 1, start, st)) return true;
                }
            }
        }

        if (state->isAc1()) {
            this->tokens.emplace_back(this->tokenMap[state], -1, script.substr(start, ind - start));
            start = ind;
            return true;
        }

        return false;

    }

    const NfaGraph &CWJLever::getGraph() const {
        return graph;
    }

    void CWJLever::clear() {
        this->tokens.clear();
    }


    const CharSet CharSet::EPSILON_CharSet(0,CharSet::INF,false);

    uint32_t CharSet::getFrom() const {
        return from;
    }

    uint32_t CharSet::getTo() const {
        return to;
    }

    bool CharSet::isOpposite1() const {
        return isOpposite;
    }

    void CharSet::setIsOpposite(bool isOpposite) {
        CharSet::isOpposite = isOpposite;
    }


}

